% -*- coding:utf-8 -*-

\section{Analizador Semántico}
\label{sec:semantico}
El analizador semántico está formado por varios componentes que se
describen en las secciones siguientes.

\subsection{Clases semánticas}
\label{subsec:semanticas}

Las \emph{Clases Semánticas} son un conjunto de clases que tienen como
objetivo almacenar la información semántica extraída del archivo de
entrada. Para cada tipo de entidad semántica (por ejemplo,
\emph{Host}, \emph{Net} o \emph{Router}) hay una clase que alberga
todos los atributos definidos para la entidad.

Además de ésas clases principales hay otras secundarias que componen a
las primeras, para el caso, por ejemplo, de que pueda repetirse varias
veces un mismo atributo, en cuyo caso dicho atributo se modela como
una clase y la clase contenedora utilizará un vector o contenedor
asociativo (según se necesite) para almacenar tantas instancias del
atributo como sea necesario.

En el listado ~\ref{lst:netthumbTypes} se muestra la parte declarativa
de dichas clases.

\lstset{
        basicstyle=\small,
        tabsize=2,
        commentstyle=\color{blue},
        keywordstyle=\bfseries,
        identifierstyle=\ttfamily,
        breaklines,
        captionpos=t,
        showstringspaces=false,
        extendedchars=true,
        frame=trBL,
	frameround=shadowbox
        xleftmargin=1.0cm,
        xrightmargin=0.5cm,
      }

\lstinputlisting[numbers=left, language=c++]{../../src/netthumb.h}
\begin{lst}
\caption{Parte declarativa de los tipos semánticos.}
\label{lst:netthumbTypes}
\end{lst}
\par

\subsection{Tabla de símbolos}
\label{subsec:symboltable}
La Tabla de Símbolos, aunque podría tener dedicada una sección para
ella sola, se entendrá mejor en éste contexto y además ayudará a
entender mejor el funcionamiento del analizador semántico.

La Tabla de Símbolos es una clase que almacena toda la información
semántica extraída del archivo de entrada. Para ello, dispone de tres
tablas hash, una por cada entidad semántica principal.

Además de ello, también dispone de métodos para la introducción de
información en dicha tabla, así como para el acceso, no solo a las
tablas de atributos de forma directa, sino que también se le puede
información correlacionada, como por ejemplo las interfaces de red
según un host dado.

~\ref{lst:symbolTable} muestra la declaración de la clase
\emph{SymbolTable}.
\newpage
\lstset{
        basicstyle=\small,
        tabsize=2,
        commentstyle=\color{blue},
        keywordstyle=\bfseries,
        identifierstyle=\ttfamily,
        breaklines,
        captionpos=t,
        showstringspaces=false,
        extendedchars=true,
        frame=trBL,
	frameround=shadowbox
        xleftmargin=1.0cm,
        xrightmargin=0.5cm,
      }

\lstinputlisting[numbers=left, language=c++]{../../src/SymbolTable.h}
\begin{lst}
\caption{Parte declarativa de la tabla de símbolos.}
\label{lst:symbolTable}
\end{lst}
\par

\subsection{Clase \emph{ContextI}}
\label{subsec:contexti}
La clase \emph{ContextI} implementa todas las funciones encargadas de
realizar los análisis semánticos. En realidad ésta clase implementa un
patrón \emph{Visitor} \cite{visitorRef} que definirá tantos métodos
\emph{visit} como clases semánticas existen (sección
~\ref{subsec:semanticas}) mas otro \emph{visit} para la tabla de
símbolos. La clase \emph{ContextI} hereda de la clase abstracta
\emph{netthumbVisitor}, que puede verse en el listado
~\ref{lst:netthumbvisitor}.

El funcionamiento de éstos métodos \emph{visit} tiene poco que contar. Cuando se
invoca un \emph{visit}, pasándole un puntero a un objeto del tipo
correspondiente recién creado, el método rellena la clase con toda la
información semántica almacenada en su tabla de atributos (previamente
rellenada desde las producciones del analizador sintáctico, como se
verá en ~\ref{subsec:semanticpr}). Una vez que la clase ha sido
rellenada, es almacenada dentro de la tabla de símbolos (ya que existe
una instancia de ésta dentro del visitor). Además, algunos
\emph{visit} puede hacer algunas comprobaciones, como por ejemplo
comprobar que no se intenta crear un host con un nombre que ya existe.

En el listado ~\ref{lst:visit} se puede ver la implementación de uno de
los \emph{visit}.

\lstset{
        basicstyle=\small,
        tabsize=2,
        commentstyle=\color{blue},
        keywordstyle=\bfseries,
        identifierstyle=\ttfamily,
        breaklines,
        captionpos=t,
        showstringspaces=false,
        extendedchars=true,
        frame=trBL,
	frameround=shadowbox
        xleftmargin=1.0cm,
        xrightmargin=0.5cm,
      }

\lstinputlisting[numbers=left, language=c++]{../../src/netthumbVisitor.h}
\begin{lst}
\caption{Clase abstracta para el visitor.}
\label{lst:netthumbvisitor}
\end{lst}
\par
\newpage
\lstset{
        basicstyle=\small,
        tabsize=2,
        commentstyle=\color{blue},
        keywordstyle=\bfseries,
        identifierstyle=\ttfamily,
        breaklines,
        captionpos=t,
        showstringspaces=false,
        extendedchars=true,
        frame=trBL,
	frameround=shadowbox
        xleftmargin=1.0cm,
        xrightmargin=0.5cm,
      }

\begin{lstlisting}
  void
  ContextI::visit(DeviceI* device)
  {

    if(_deviceMap.count(_attrMap[DEVICE_ID])){
      ostringstream errMsg;
      errMsg << "Warning: Device \'" << _attrMap[DEVICE_ID] << "\' declared in ";
      errMsg << _attrMap[DEVICE_LOCATION] << " was previously declared in ";
      errMsg << _deviceMap[_attrMap[DEVICE_ID]]->getLocation() << "; skipping";
      _driver->error(errMsg.str());
      return;
    }

    map<string, LinkSt> linkmap;
    map<string, NetScope> scopemap;

    linkmap[string("nullSt")] = nullSt;
    linkmap[string("up")] = up;
    linkmap[string("down")] = down;

    scopemap[string("null")] = nullScope;
    scopemap[string("global")] = global;
    scopemap[string("site")] = site;
    scopemap[string("link")] = link;
    scopemap[string("host")] = host;

    device->populate(_attrMap[DEVICE_ADDRID],
		     _attrMap[DEVICE_NETID],
		     _attrMap[DEVICE_LINKID],
		     _attrMap[DEVICE_LOCATION]);

    if(_attrMap.count(DEVICE_LINKST))
      device->setLinkSt(linkmap[_attrMap[DEVICE_LINKST]]);

    if(_attrMap.count(DEVICE_NETSCOPE))
      device->setScope(scopemap[_attrMap[DEVICE_NETSCOPE]]);

    // Almacena la instancia en la lista de Devices.
    _deviceMap[_attrMap[DEVICE_ID]] = device;

    // Limpia la tabla de atributos
    _attrMap.clear();
  }
\end{lstlisting}
\begin{lst}
\caption{Implementación de uno de los \emph{visit}.}
\label{lst:visit}
\end{lst}
\par

El último \emph{visit} que se ejecuta corresponde a la tabla de
símbolos propiamente dicha. En éste \emph{visit} se valida la tabla de
símbolos comprobando que no existen errores semánticos. Para ello se
van invocando secuencialmente una serie de rutinas que verifican la
integridad de la tabla de símbolos. Si todas las comprobaciones son
correctas, el atributo \emph{\_valid} de la tabla de símbolos se pone
a \emph{true} indicando que los datos que contiene son correctos. Si
\emph{\_valid} es \emph{false}, se han producido errores semánticos,
con lo que se aborta la compilación.

El listado ~\ref{lst:visitst} muestra \emph{visit} de la tabla de
símbolos y ~\ref{lst:test} muestra un ejemplo de una de las funciones
que analiza la tabla de símbolos.

\lstset{
        basicstyle=\small,
        tabsize=2,
        commentstyle=\color{blue},
        keywordstyle=\bfseries,
        identifierstyle=\ttfamily,
        breaklines,
        captionpos=t,
        showstringspaces=false,
        extendedchars=true,
        frame=trBL,
	frameround=shadowbox
        xleftmargin=1.0cm,
        xrightmargin=0.5cm,
      }

\begin{lstlisting}
  void
  ContextI::visit(SymbolTable* stable)
  {
    stable->_valid = checkNetInDevices(stable) && checkNetInRoutes(stable);
  }
\end{lstlisting}
\begin{lst}
\caption{Implementación del \emph{visit} de la tabla de simbolos.}
\label{lst:visitst}
\end{lst}
\par

\lstset{
        basicstyle=\small,
        tabsize=2,
        commentstyle=\color{blue},
        keywordstyle=\bfseries,
        identifierstyle=\ttfamily,
        breaklines,
        captionpos=t,
        showstringspaces=false,
        extendedchars=true,
        frame=trBL,
	frameround=shadowbox
        xleftmargin=1.0cm,
        xrightmargin=0.5cm,
      }

\begin{lstlisting}
  bool
  ContextI::checkNetInRoutes(SymbolTable* stable)
  {
    /*
      Comprueba que todas las redes presentes
      en las tablas de rutas existan.
    */

    bool ok = true;

    map<string, NetworkI*> networks = stable->getNetworks();

    // Comprobacion de los hosts.
    map<string, HostI*>::iterator hostIt;
    for(hostIt = stable->getHosts().begin();
	hostIt != stable->getHosts().end();
	hostIt++) {

      vector<ConfRouteI*>::iterator hconfIt;
      for(hconfIt = hostIt->second->getRoute()->getConfigurations().begin();
	  hconfIt != hostIt->second->getRoute()->getConfigurations().end();
	  hconfIt++) {

	if(networks.count((*hconfIt)->getNetID()) == 0){
	  // Error: Network no declarada.
	  ostringstream errOut;
	  errOut << "Error in Host \'" << hostIt->first << "\' (";
	  errOut << (*hconfIt)->getLocation() << "): Network \'" << (*hconfIt)->getNetID();
	  errOut << "\' not declared";
	  _driver->error(errOut.str());	  
	  ok = false;
	}
      }
    }

    // Comprobacion de los routers.
    map<string, RouterI*>::iterator routerIt;
    for(routerIt = stable->getRouters().begin();
	routerIt != stable->getRouters().end();
	routerIt++) {

      vector<ConfRouteI*>::iterator hconfIt;
      for(hconfIt = routerIt->second->getRoute()->getConfigurations().begin();
	  hconfIt != routerIt->second->getRoute()->getConfigurations().end();
	  hconfIt++) {

	if(networks.count((*hconfIt)->getNetID()) == 0){
	  // Error: Network no declarada.
	  ostringstream errOut;
	  errOut << "Error in Router \'" << routerIt->first << "\' (";
	  errOut << (*hconfIt)->getLocation() << "): Network \'" << (*hconfIt)->getNetID();
	  errOut << "\' not declared";
	  _driver->error(errOut.str());	  
	  ok = false;
	}
      }
    }
    return ok;
  }
\end{lstlisting}
\begin{lst}
\caption{Implementación de uno de los tests para la tabla de símbolos.}
\label{lst:test}
\end{lst}
\par

El hecho de implementar de ésta manera el analizador semántico permite
que pueda extenderse fácilmente la semántica del lenguaje
implementando un nuevo \emph{visitor} (heredando de
\emph{netthumbVisit}) o extender el ya existente con nuevas funciones
que comprueben distintos aspectos semánticos.

\subsection{Producciones en el analizador sintáctico}
\label{subsec:semanticpr}
La implementación del analizador sintáctico (sección
~\ref{sec:sintactico}) contiene código asociado a algunas de las
producciones que se encarga de rellenar la tabla de atributos del
\emph{visitor} para el analizador semántico (sección
~\ref{subsec:contexti}). Para cada producción que reconoce algun
atributo se almacena dicho atributo en la tabla de atributos (como se
puede ver en el ejemplo del listado ~\ref{lst:recAttr}). Si lo que se
reconoce es una entidad (por ejemplo un host, como en el ejemplo del
listado ~\ref{lst:recHost}), se crea una nueva clase, correspondiente
a la entidad detectada, se obtiene la información sobre en qué
posición del archivo se ha reconocido la entidad (que también se
almacena para los posibles mensajes de error) y se invoca al
correspondiente \emph{visit}, que almacenará el correspondiente
objeto.
\newpage
\lstset{
        basicstyle=\small,
        tabsize=2,
        commentstyle=\color{blue},
        keywordstyle=\bfseries,
        identifierstyle=\ttfamily,
        breaklines,
        captionpos=t,
        showstringspaces=false,
        extendedchars=true,
        frame=trBL,
	frameround=shadowbox
        xleftmargin=1.0cm,
        xrightmargin=0.5cm,
      }

\begin{lstlisting}
onOff:
    NETTHUMB_ON
    {
      driver.getContext().insertAttribute(NETWORK_MULTICAST, string("on"));
    }
|
    NETTHUMB_OFF
    {
      driver.getContext().insertAttribute(NETWORK_MULTICAST, string("off"));
    }
;
\end{lstlisting}
\begin{lst}
\caption{Reconocimiento de un atributo en el analizador sintáctico.}
\label{lst:recAttr}
\end{lst}
\par

\lstset{
        basicstyle=\small,
        tabsize=2,
        commentstyle=\color{blue},
        keywordstyle=\bfseries,
        identifierstyle=\ttfamily,
        breaklines,
        captionpos=t,
        showstringspaces=false,
        extendedchars=true,
        frame=trBL,
	frameround=shadowbox
        xleftmargin=1.0cm,
        xrightmargin=0.5cm,
      }

\begin{lstlisting}
host:
     NETTHUMB_HOST ID INICIO_BLOQUE devices route FIN_BLOQUE
     {
       driver.getContext().insertAttribute(HOST_ID, *$2);
       std::ostringstream buffer;
       buffer << @1;
       std::string location = buffer.str();
       driver.getContext().insertAttribute(HOST_LOCATION, 
					   location.substr(0, location.find_first_of("-")));
       driver.getContext().visit(new HostI());
     }
\end{lstlisting}
\begin{lst}
\caption{Reconocimiento del Host.}
\label{lst:recHost}
\end{lst}
\par

\subsection{Test del análisis}
\label{subsec:testAnalisis}

Para testear la detección de los posibles errores se ha utilizado un
archivo con la siguiente descripción de una topología:

\lstset{
        basicstyle=\small,
        tabsize=2,
        commentstyle=\color{blue},
        keywordstyle=\bfseries,
        identifierstyle=\ttfamily,
        breaklines,
        captionpos=t,
        showstringspaces=false,
        extendedchars=true,
        frame=trBL,
	frameround=shadowbox
        xleftmargin=1.0cm,
        xrightmargin=0.5cm,
      }

\lstinputlisting[numbers=left]{../../src/example.txt}
\begin{lst}
\caption{Ejemplo de entrada con errores semánticos.}\
\label{lst:exampleError}
\end{lst}
\par

Tras el análisis del listado ~\ref{lst:exampleError} se obtiene la
siguiente salida:

\lstset{
        basicstyle=\small,
        tabsize=2,
        commentstyle=\color{blue},
        keywordstyle=\bfseries,
        identifierstyle=\ttfamily,
        breaklines,
        captionpos=t,
        showstringspaces=false,
        extendedchars=true,
        frame=trBL,
	frameround=shadowbox
        xleftmargin=1.0cm,
        xrightmargin=0.5cm,
      }


\begin{lstlisting}
Warning: Device 'eth0' declared in example.txt:7.4 was previously declared in example.txt:2.4; skipping
Warning: Device 'eth0' declared in example.txt:34.4 was previously declared in example.txt:29.4; skipping
Warning: Host 'unhostdeprueba' declared in example.txt:28.1 was previously declared in example.txt:1.1; skipping
Warning: Device 'eth0' declared in example.txt:55.8 was previously declared in example.txt:29.4; skipping
Error in Host 'unhostdeprueba' (example.txt:2.4): Network 'N1' in Device 'eth0' not declared
Error in Host 'unhostdeprueba' (example.txt:12.4): Network 'N2' in Device 'wlan0' not declared
Error in Router 'router1' (example.txt:54.1): Network 'N1' in Device 'eth0' not declared
Error in Router 'router1' (example.txt:54.1): Network 'N11' in Device 'eth1' not declared
Error in Router 'router1' (example.txt:54.1): Network 'N2' in Device 'wlan0' not declared
example.txt contains errors
\end{lstlisting}
\begin{lst}
\caption{Salida de error.}
\label{lst:outError}
\end{lst}
\par
